package reports.servlets;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;



import com.crystaldecisions.sdk.occa.report.application.DBOptions;
import com.crystaldecisions.sdk.occa.report.application.ReportClientDocument;
import com.crystaldecisions.sdk.occa.report.data.IConnectionInfo;
import com.crystaldecisions.sdk.occa.report.data.IParameterField;
import com.crystaldecisions.sdk.occa.report.data.ParameterFieldDiscreteValue;
import com.crystaldecisions.sdk.occa.report.exportoptions.ReportExportFormat;
import com.crystaldecisions.sdk.occa.report.lib.ReportSDKParameterFieldException;
import com.crystaldecisions.sdk.occa.report.lib.PropertyBag;
import com.crystaldecisions.sdk.occa.report.data.Fields;



/**
 * Servlet implementation class ReportServletEngine
 */
@WebServlet(description = "Servlet para la generacion de reportes", urlPatterns = { "/ReportServletEngine" })
public class ReportServletEngine extends HttpServlet {
	
	
	private static String TRUSTED_CONNECTION = "false";
	//private static String PREQESERVERName = "jdbc:sqlserver://127.0.0.1:1433";
	private static String PREQESERVERName = "jdbc:mysql://127.0.0.1:3306/setsuvapp";
	private static String SERVER_TYPE = "JDBC (JNDI)";
	private static String DATABASE_DLL = "crdb_jdbc.dll";
	private static String DATABASE = "setsuvapp";
	//private static String DATABASE_CLASS_NAME = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	private static String DATABASE_CLASS_NAME = "com.mysql.jdbc.Driver";
	private static String USE_JDBC = "true";
	private static String DATABASE_NAME = "setsuvapp";
	private static String SERVER_NAME = "jdbc:mysql://127.0.0.1:3306/setsuvapp";
	private static String JNDI_DATASOURCE_NAME = "site";
	private static String CONNECTION_URL = "jdbc:mysql://127.0.0.1:3306/setsuvapp";
	private static String SERVER = "127.0.0.1";

	
	
	
	private static final long serialVersionUID = 1L;
    private static final String URL_RELATIVA = "/WEB-INF/reportes/";
    
    private List<ParametroReporte> listaParametros;
    private ParametroReporte dbParameter;
    private InputStream inputStream; 
  
    public ReportServletEngine() {
        super();
         
    }

	
	public void init(ServletConfig config) throws ServletException {
		try{
			
			Properties prop = new Properties();
			InputStream in = ReporteServlet.class.getResourceAsStream("/config/reportconfig.properties");
			prop.load(in);			
			dbParameter = new ParametroReporte();
			dbParameter.setParametro("database");
			dbParameter.setValor(prop.getProperty("report.source.default"));			
			in.close();
			in = null;
			prop = null;		
			
		}catch(Exception e){
			e.printStackTrace();
			throw new ServletException("Ocurrio un error al inicializar ReportServletEngine.-"+ e.getMessage() + e);
		}			
	}

	
	public void destroy() {
		
		try {
			
			listaParametros.clear();
			listaParametros = null;
			dbParameter  = null;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
	}

	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		procesarPeticion(request,response);
	}

	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		procesarPeticion(request,response);
	}

	
	
	protected void procesarPeticion(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String contentType = null;
		String fileName = null;
		ReportExportFormat reportExportFormat = null;
				
		
		if(generarAtributosReporte(request, "formato")== null){
			contentType = "application/pdf";
			fileName = "reporte.pdf";
			reportExportFormat = ReportExportFormat.PDF;			
		}else{
			if(generarAtributosReporte(request, "formato").equals("pdf")){			
				contentType = "application/pdf";
				fileName = "reporte.pdf";
				reportExportFormat = ReportExportFormat.PDF;
			}else{			
				if(generarAtributosReporte(request, "formato").equals("doc")){
					contentType = "application/msword";
					fileName = "reporte.doc";
					reportExportFormat = ReportExportFormat.MSWord;
				}else{				
					if(generarAtributosReporte(request, "formato").equals("xls") ){
						contentType = "application/vnd.ms-excel";
						fileName = "reporte.xls";
						reportExportFormat = ReportExportFormat.MSExcel;
					}else{					
						if(generarAtributosReporte(request, "formato").equals("img") ){
							contentType = "image/jpeg";
							fileName = "reporte.img";
							reportExportFormat = ReportExportFormat.PDF;
						}else{					
							response.sendError(501,"Formato de reporte invalido");
							return;
						}					
					}
				}
			}			
		}		
		
		ReportClientDocument reportClientDocument  = null;
		
		//Se realiza la apertura del reporte para poder acceder a sus propiedades
		try{		
			
			StringBuilder sbUrl = new StringBuilder(URL_RELATIVA);
			
			if(generarAtributosReporte(request, "folder") != null){				
				sbUrl.append(generarAtributosReporte(request, "folder")).append("/");				
			}
			
			if( generarAtributosReporte(request, "nombre") != null){				
				sbUrl.append(generarAtributosReporte(request, "nombre"));
			}else{
				throw new Exception("El nombre del reporte no fue especificado",null);				
			}
			
			System.out.println("reporte -> " + sbUrl);
			
			this.listaParametros = generarParametros(request);
			
			System.out.println("Parametros del reporte: " + this.listaParametros);	
			
			
			//System.out.println("generando clientDocument ");
			reportClientDocument = new ReportClientDocument();
			
			//System.out.println("abriendo documento!!!");			
			reportClientDocument.open(sbUrl.toString(), 0);
			
			
				
			/*for(ParametroReporte parametroReporte : this.listaParametros){
				reportClientDocument.getDataDefController().getParameterFieldController()
					.setCurrentValue("", parametroReporte.getParametro(), parametroReporte.getValor());
			}
			
			//Se sustituye esta linea para no usar la referencia de clase paramController y se usa el campo dbParameter
			//paramController.setCurrentValue("", "database", prop.getProperty("report.source.default"));
			reportClientDocument.getDataDefController().getParameterFieldController()
				.setCurrentValue("", this.dbParameter.getParametro(), this.dbParameter.getValor());
			
			reportClientDocument.getDatabaseController().logon("sa", "F3C47rywsh3r3i");
			*/			
			/****/
			
			/*Fields fields = reportClientDocument.getDataDefinition().getParameterFields();
			
			System.out.println("CAMPOS de la conexion ... ");
			
			
			for (int i = 0 ; i < fields.size(); i ++){
				
				IParameterField pf = (IParameterField) fields.getField(i);
				System.out.print("Campo " + i + ": " + pf.getName());
				
				ParameterFieldDiscreteValue pfdv = (ParameterFieldDiscreteValue) pf.getValues().get(0);
				System.out.print("| Valor " + i + ": " + pfdv.getValue());
			}*/
			
			
			IConnectionInfo connInfo = reportClientDocument.getDatabaseController().getConnectionInfos(null).getConnectionInfo(0);
			
			
			System.out.println("Info PREVIA de la conexion ... ");
			System.out.println(connInfo.getAttributes());
			
			connInfo.getAttributes().clear();
			
			PropertyBag propertyBag = new PropertyBag();

			propertyBag.put("Trusted_Connection",TRUSTED_CONNECTION);
			propertyBag.put("PreQEServerName",PREQESERVERName);
			propertyBag.put("Server Type",SERVER_TYPE);
			propertyBag.put("Database DLL",DATABASE_DLL);
			propertyBag.put("Database",DATABASE);
			propertyBag.put("Database Class Name",DATABASE_CLASS_NAME);
			propertyBag.put("Use JDBC",USE_JDBC);
			propertyBag.put("Database Name",DATABASE_NAME);
			propertyBag.put("Server Name",SERVER_NAME);
			propertyBag.put("JNDI Datasource Name",JNDI_DATASOURCE_NAME);
			propertyBag.put("Connection URL",CONNECTION_URL);
			propertyBag.put("Server",SERVER);
			
			connInfo.setAttributes(propertyBag);
			//connInfo.setUserName("sa");
			//connInfo.setPassword("F3C47rywsh3r3i");
			connInfo.setUserName("appadmin");
			connInfo.setPassword("admin");
			
			
			int replaceParams = DBOptions._ignoreCurrentTableQualifiers + DBOptions._doNotVerifyDB;
			
			reportClientDocument.getDatabaseController().replaceConnection(
					reportClientDocument.getDatabaseController().getConnectionInfos(null).getConnectionInfo(0), connInfo , null, replaceParams);
			
			
			IConnectionInfo connInfo2 = reportClientDocument.getDatabaseController().getConnectionInfos(null).getConnectionInfo(0);
			
			System.out.println("Info POSTERIOR de la conexion ... ");
			System.out.println(connInfo2.getAttributes());
			
			
			for(ParametroReporte parametroReporte : this.listaParametros){
				reportClientDocument.getDataDefController().getParameterFieldController()
					.setCurrentValue("", parametroReporte.getParametro(), parametroReporte.getValor());
			}
			
			//Se sustituye esta linea para no usar la referencia de clase paramController y se usa el campo dbParameter
			//paramController.setCurrentValue("", "database", prop.getProperty("report.source.default"));
			
			/*
			reportClientDocument.getDataDefController().getParameterFieldController()
				.setCurrentValue("", this.dbParameter.getParametro(), this.dbParameter.getValor());
			*/
			
			//reportClientDocument.getDatabaseController().logon("sa", "F3C47rywsh3r3i");

			/****/
			response.setCharacterEncoding("UTF-8"); 
			
			response.setHeader("Content-disposition", "inline;filename=" + fileName);
			
			response.setContentType(contentType);			
			
			//Se declara como variable de referencia;
			//InputStream inputStream = this.reportClientDocument.getPrintOutputController().export(reportExportFormat);		
			this.inputStream = reportClientDocument.getPrintOutputController().export(reportExportFormat);
			
			
			if(contentType.equals("image/jpeg")){
				
				
				PDDocument pdDocument = PDDocument.load(inputStream);			
				
				BufferedImage originalImage = ((PDPage)pdDocument.getDocumentCatalog()
												.getAllPages().get(0)).convertToImage(BufferedImage.TYPE_3BYTE_BGR,350);
								
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				
				ImageIO.write( originalImage, "jpg", baos );
				 
				baos.flush();				
				
				byte[] buffer = baos.toByteArray();
				
				baos.close();
				pdDocument.close();
				originalImage = null;
				baos = null;
				pdDocument = null;
				
				response.getOutputStream().write(buffer);
				
				buffer = null;
			
			}else{
				
				byte[] buffer = new byte[inputStream.available()];		
				
				int bytesRead = 0;
				
				while((bytesRead = inputStream.read(buffer)) != -1) {
					response.getOutputStream().write(buffer, 0, bytesRead);	
				}
				
				buffer = null;
			}
	
			response.getOutputStream().flush();
			response.getOutputStream().close();		
			
			//Se cierra el acceso a las propiedades del reporte
			inputStream.close();
			reportClientDocument.close();
			
		}catch(ReportSDKParameterFieldException rfe){
			rfe.printStackTrace();
			System.out.println(rfe.getMessage());
			response.sendError(501, rfe.getMessage());
		}
		catch(Exception e){
			e.printStackTrace();			
			response.sendError(500, e.getMessage());
		}finally{
			System.out.println("contentType -> " + contentType);
			contentType = null;			
			fileName = null;
			reportExportFormat = null;		
			inputStream = null;			
			reportClientDocument = null;
			System.out.println("reporte generado!!!");
			System.out.println(" ----------------------------");
		}		
	}
	
	
	private List<ParametroReporte> generarParametros(HttpServletRequest request){
		
		Enumeration<String> nombresParam = request.getParameterNames();

		List<ParametroReporte> parametrosLista  = new ArrayList<ParametroReporte>();
		
		while(nombresParam.hasMoreElements()){
			
			String parametro = nombresParam.nextElement();
		
			if(parametro.split("_")[0].equals("prm")){
			
				ParametroReporte pr = new ParametroReporte();
				
				pr.setParametro( parametro.split("_")[1] );
				pr.setValor( request.getParameter(parametro) );
				parametrosLista.add(pr);
				
				pr = null;
			}
			
			parametro = null;
		}	
		
		nombresParam = null; 	
		
		return parametrosLista;
	}

	private String generarAtributosReporte(HttpServletRequest request, String atributo){
		
		Enumeration<String> nombresParam = request.getParameterNames();
		String atributo_ = null;
		
				
		while(nombresParam.hasMoreElements()){
			
			String parametro = nombresParam.nextElement();
				
			if(parametro.split("_")[0].equals("rpt")){
				
				String nombreParam = parametro.split("_")[1];
				
				if(nombreParam.equals(atributo)){
					
					atributo_ = request.getParameter(parametro);
		
				}		
				
				nombreParam = null;
			}
			parametro = null;
		}	
		
		nombresParam = null;
		
		return atributo_;	
	}

	private class ParametroReporte{
		
		private String parametro;
		
		private String valor;
		
		public String getParametro() {
			return parametro;
		}
		
		public void setParametro(String parametro) {
			this.parametro = parametro;
		}
		
		public String getValor() {
			return valor;
		}
		
		public void setValor(String valor) {
			this.valor = valor;
		}
		
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return getParametro() + ":" + getValor();
		}
	}
	
}
